/*********** SERVIDOR PRINCIPAL ************/
/* Encoding: UTF8 */

#include <sys/types.h>   // Definição de tipos
#include <sys/socket.h>  // Biblioteca de estrutura de sockets
#include <netinet/in.h>  // Define os padroes de protocolo IP
#include <arpa/inet.h>   // Converte enderecos hosts
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h> // Define constantes POSIX
#include <errno.h>  // Utilizado para capturar mensagens de erro
#include <sys/time.h>

void concatena(int a[],int b[],int c[]){   
  int i;
   
  for (i=0;i<5000;i++)
  {
    c[i] = a[i];
    c[i+5000] = b[i] ; 
  }
}


void quickSort( int a[], int l, int r)
{
   int j;

   if( l < r ) 
   {
    // divide and conquer
        j = partition( a, l, r);
       quickSort( a, l, j-1);
       quickSort( a, j+1, r);
   } 
}

int partition( int a[], int l, int r) {
   int pivot, i, j, t;
   pivot = a[l];
   i = l; j = r+1;
    
   while( 1)
   {
    do ++i; while( a[i] <= pivot && i <= r );
    do --j; while( a[j] > pivot );
    if( i >= j ) break;
    t = a[i]; a[i] = a[j]; a[j] = t;
   }
   t = a[l]; a[l] = a[j]; a[j] = t;
   return j;
}

int main(int argc, char *argv[])
{   
  // Variáveis
  int sock, connected[2], i, true = 1, connections = 0;
  int vectorin[2][5000];
  int sortedvector[10000];
  struct sockaddr_in server_addr, client_addr;
  int sin_size;

  // Contadores de tempo
  struct timeval ordering_time_begin, ordering_time_end;
  struct timeval connection_time_begin[2], connection_time_end[2];
  double ordering_time_spent, connection_time_spent[2];

  /* Checa argumentos da linha de comando
     argv[0] = Nome do aplicativo
     argv[1] = Porta do servidor que será usada pelos servidores nível 2
  */
  if(argc < 2){
    printf("Uso : %s <porta servidor segundo nivel>\n", argv[0]);
    exit(1);
  }

  // Cria socket
  if ((sock = socket(AF_INET, SOCK_STREAM, 0)) == -1)
  {
    perror("Erro no Socket");
    exit(1);
  }

  if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &true,sizeof(int)) == -1)
  {
    perror("Erro Setsockopt");
    exit(1);
  }


  // Configura o endereco deste servidor
  server_addr.sin_family = AF_INET;
  server_addr.sin_port = htons(atoi(argv[1]));
  server_addr.sin_addr.s_addr = INADDR_ANY;
  bzero(&(server_addr.sin_zero),8);


  // Informa endereço ao socket
  if (bind(sock, (struct sockaddr *)&server_addr, sizeof(struct sockaddr)) == -1)
  {
    perror("Nao foi possivel realizar o bind");
    exit(1);
  }

  // Configura o socket para aguardar mensagens
  if (listen(sock, 10) == -1)
  {
    perror("Erro de Listen");
    exit(1);
  }

  // Variável para armazenar o tamanho de endereço do cliente conectado
  sin_size = sizeof(struct sockaddr_in);

  // Loop para receber os vetores dos dois servidores nível 2
  while(connections < 2 && (connected[connections] = accept(sock, (struct sockaddr *)&client_addr, &sin_size)))
  {
    // Inicia contador de tempo de conexão com servidores nível 2
    gettimeofday(&connection_time_begin[connections], NULL);
    // Aguarda o recebimento do vetor
    recv(connected[connections], vectorin[connections], sizeof(int)*5000, 0);
    // Termina contador de tempo de conexão com servidores nível 2
    gettimeofday(&connection_time_end[connections], NULL);

    printf("Vetor recebido: [ ");
    for(i = 0; i < 5000; i++)
    {
      printf("%d ", vectorin[connections][i]);
    }
    printf("]\n");

    connections++;
    sleep(1);
  }

  // Inicia contagem do tempo de ordenação do vetor
  gettimeofday(&ordering_time_begin, NULL);
  // Concatena e ordena os dois vetores
  concatena(vectorin[0], vectorin[1], sortedvector);
  quickSort(sortedvector, 0, 9999);
  // Termina contagem do tempo de ordenação do vetor
  gettimeofday(&ordering_time_end, NULL);

  printf("Vetor enviado: [ ");
  for(i = 0; i < 10000; i++)
  {
    printf("%d ", sortedvector[i]);
  }
  printf("]\n");
  // Envia o vetor de resposta com 10000 elementos ordenados aos servidores nível 2
  for(i = 0; i < connections; i++)
  {
    send(connected[i], sortedvector, sizeof(int)*10000, 0);
    close(connected[i]);
  }
  // Fecha o socket
  close(sock);

  ordering_time_spent = ((ordering_time_end.tv_sec - ordering_time_begin.tv_sec)*10000000L + ordering_time_end.tv_usec) - ordering_time_begin.tv_usec;
  for (i = 0; i < connections; i++)
    connection_time_spent[i] = ((connection_time_end[i].tv_sec - connection_time_begin[i].tv_sec)*10000000L + connection_time_end[i].tv_usec) - connection_time_begin[i].tv_usec;
  printf("Tempo de ordenação do vetor: %.2lf ms\n", ordering_time_spent / 10000);
  for (i = 0; i < connections; i++)
    printf("Tempo de conexão com o servidor %d: %.2lf ms\n", i, connection_time_spent[i] / 10000);

  return 0;
}
